/*************************************************************
   Copyright(C) 2017 by MartyrCompany
   All rights reserved.
   
   CtrlItemData.cs
   UnityFramework
   
   Created by WuIslet on 2022-05-31.
   
*************************************************************/

using System;
using System.Collections.Generic;
using UnityEngine;

[Serializable]
public class CtrlItemData
{
    public const string EMPTY_TARGET_PATH = "$<-:Empty:->$";
    public const int BINDNULLINDEX = 30;
    public int index = -1;
    public string TargetFileID = "";
    public string name = string.Empty;
    public string arg = string.Empty;
    public bool[] extraGeneratorFunc = new bool[BINDNULLINDEX + 1]; //True表示不生成额外函数，False表示生成额外函数
    public string[] extraFunc = new string[BINDNULLINDEX + 1];
    public bool isList = false;
    public string listName = string.Empty;
    public bool isNotGeneratorCode = false;
    public string type = string.Empty;
    public string targetsPath = "";

    public bool IsHasExtraArg()
    {
        if (type == EUIControlDataType.RedDot)
            return true;
        if (type == EUIControlDataType.OSALScrollView)
            return true;
        if (type == EUIControlDataType.OSALGridView)
            return true;
        if (type == EUIControlDataType.OSALScrollViewLazy)
            return true;
        if (type == EUIControlDataType.OSALGridViewLazy)
            return true;
        if (type == EUIControlDataType.Child)
            return true;
        if (type == EUIControlDataType.PanelView)
            return true;
        return false;
    }

    #region BindArgs

    [SerializeField] private long BindFlag = 0;
    public enum BindType
    {
        Active = 0x1,
        Interactable = 0x2,
        Color = 0x4,
        Alpha = 0x8,
        Lock = 0x10,
        Select = 0x20,
    }

    [NonSerialized] public static readonly List<BindType> BindAllTypes = new List<BindType>() {BindType.Active, BindType.Interactable, BindType.Color, BindType.Alpha, BindType.Lock, BindType.Select };
    public bool GetBindFlag(BindType type)
    {
        return (BindFlag & ((long)type)) > 0;
    }

    public void SetBindFlag(BindType type, bool flag)
    {
        if (GetBindFlag(type) == flag)
            return;

        if (flag)
        {
            BindFlag |= (long)type;
        }
        else
        {
            BindFlag &= (~(long)type);
        }
    }

    private long CheckDefaultBindType(string type)
    {
        return 0;
    }

    public static string GetCSharpBindCode(BindType type)
    {
        switch (type)
        {
            case BindType.Active:
                return ".gameObject.SetActive((bool)arg);";
            case BindType.Interactable:
                return ".interactable = arg;";
            case BindType.Color:
                return ".color = arg;";
            case BindType.Alpha:
                return ".color = arg;";
            case BindType.Lock:
                return "TODO"; //TODO
            case BindType.Select:
                return "TODO"; //TODO
            default:
                return "";
        }
    }

    public static string GetLuaBindCode(BindType type)
    {
        switch (type)
        {
            case BindType.Active:
                return ":setVisible(arg)";
            case BindType.Interactable:
                return ":setEnable(arg)";
            case BindType.Color:
                return ":setColor(arg)";
            case BindType.Alpha:
                return ":setColorAlpha(arg)";
            default:
                return "";
        }
    }

    public static bool CheckTypeCanUseBindType(string type, BindType bindType)
    {
        switch (bindType)
        {
            case BindType.Active:
                return true;
            case BindType.Interactable:
            case BindType.Lock:
            case BindType.Select:
                if (type == EUIControlDataType.Button)
                    return true;
                else
                    return false;
            case BindType.Color:
            case BindType.Alpha:
                if (type == EUIControlDataType.Image || type == EUIControlDataType.RawImage || type == EUIControlDataType.Text || type == EUIControlDataType.TextMeshProUGUI)
                    return true;
                else
                    return false;
            default:
                return false;
        }
    }
    #endregion BindArgs

    #region Target
    public bool IsHasTarget()
    {
        return !string.IsNullOrEmpty(TargetFileID);
    }

    /// <summary>
    /// 当前映射的组件对象。
    /// 是当前工具绘制的目标，如果丢失追踪，拿到的是null。
    /// </summary>
    /// <param name="root"></param>
    /// <param name="isRefresh"></param>
    /// <param name="onlyGO"></param>
    /// <returns></returns>
    public UnityEngine.Object GetTargets(GameObject root, bool isRefresh = false, bool onlyGO = false)
    {
        if (root == null)
            return null;

        if (!isRefresh && string.IsNullOrEmpty(TargetFileID))
            return null;
        
        var t = (targetsPath == "") ? root.transform : root.transform.Find(targetsPath);

        if (t == null)
            return null;

        if (onlyGO)
            return t.gameObject;

        if (string.IsNullOrEmpty(type))
        {
            return t.gameObject;
        }
        else
        {
            var index = UIControlDataType.FindType(type);
            if (index < 0)
            {
                return null;
            }
            else
            {
                var typeList = UIControlDataType.GetList();
                if (typeList[index].unityType == typeof(GameObject))
                    return t.gameObject;
                else
                    return t.GetComponent(typeList[index].unityType);
            }
        }
    }

    public void SetTarget(GameObject go, string newType, bool isReplace = false)
    {
        if (go == null)
        {
            targetsPath = "";
            type = EUIControlDataType.None;
            if (!isReplace)
                BindFlag = CheckDefaultBindType(type);
            TargetFileID = "";
            return;
        }

        // set go
        targetsPath = go.transform.GetUIRelativePath();

        //set type
        var correctComponent = FindCorrectComponent(go, newType, out newType);
        type = newType;
        if (!isReplace)
            BindFlag = CheckDefaultBindType(type);

        //set fileID
        var goid = UnityEditor.GlobalObjectId.GetGlobalObjectIdSlow(go);
        if (goid.targetObjectId != 0)
            TargetFileID = goid.ToString();
        else
            TargetFileID = "";
    }

    public void RefreshTarget(GameObject root)
    {
        var target = GetTargets(root, true); //TODO 这里是初始化刷targetFileID，不应该拿绘制的目标吧。
        if (target != null)
        {
            var t = (targetsPath == "") ? root.transform : root.transform.Find(targetsPath);
            var go = t == null ? null : t.gameObject;
            var goid = UnityEditor.GlobalObjectId.GetGlobalObjectIdSlow(go);
            if (goid.targetObjectId != 0)
                TargetFileID = goid.ToString();
            else
                TargetFileID = "";
        }
    }

    private UnityEngine.Object FindCorrectComponent(GameObject go, string typename, out string correctType)
    {
        if (typename == EUIControlDataType.GameObject)
        {
            correctType = EUIControlDataType.GameObject;
            return go;
        }

        List<Component> components = new List<Component>();
        go.GetComponents(components);

        var list = UIControlDataType.GetList();
        for (int i = 0; i < list.Count; ++i)
        {
            var findType = list[i];
            if (typename != EUIControlDataType.None && typename != findType.name) //类型名为空则为自动类型，在 _typeMap 里从上往下找 // 指定了类型名则只找指定类型的控件
                continue;

            if (findType.unityType == typeof(GameObject))
            {
                correctType = findType.name;
                return go;
            }
            else
            {
                for (int j = 0; j < components.Count; ++j)
                {
                    var comp = components[j];
                    Type compType = comp.GetType();
                    if (compType == findType.unityType || compType.IsSubclassOf(findType.unityType))
                    {
                        correctType = findType.name;
                        return comp;
                    }
                }
            }
        }
        correctType = EUIControlDataType.GameObject;
        return go;
    }
    #endregion Target

    #region EditorTools
    /// <summary>
    /// 将保存的数据规范化到prefab上
    /// </summary>
    public void AutoFormatPrefab(GameObject root)
    {
        var obj = GetTargets(root);
        if (obj == null)
            return;

        var tag = "UI_ControlData";
        var go = obj as GameObject;
        if (go == null)
        {
            var com = obj as Component;
            if (com != null)
            {
                go = com.gameObject;
            }
        }
        if (go != null)
        {
            go.name = CtrlItemDataDefaultFormatHelper.ToUpperFirst(name);
            go.tag = tag;
            SetTarget(go, type);
        }
    }

    /// <summary>
    /// 将控件切换到指定类型
    /// </summary>
    /// <param name="typeIdx"></param>
    public bool ChangeControlsTypeTo(string typeName, string parentName, GameObject root)
    {
        var typeIdx = UIControlDataType.FindType(typeName);
        var data = UIControlDataType.GetList()[typeIdx];
        bool isGameObject = data.unityType == typeof(GameObject);

        UnityEngine.Object obj = GetTargets(root);
        if (obj == null)
        {
            Debug.LogErrorFormat("[{0}.{1}] control is null", parentName, name);
            return false;
        }

        if (obj.GetType() != typeof(GameObject))
        {
            if ((obj as Component) == null)
            {
                Debug.LogErrorFormat("[{0}.{1}] control[{2}] must be GameObject or a Component", parentName, name, obj.name);
                return false;
            }
            obj = (obj as Component).gameObject;
        }

        GameObject go = obj as GameObject;
        if (!isGameObject)
        {
            var target = go.GetComponent(data.unityType);
            if (target == null)
            {
                Debug.LogErrorFormat("[{0}.{1}] control[{2}] isn't a {3}", parentName, name, go.name, data.name);
                return false;
            }
        }
        SetTarget(go, data.name);
        return true;
    }

    public enum SearchModePreferencesEditorWindow { 全部, 变量名, 路径, 类型, /*设置*/ }
    public bool CheckSearchText(string searchText, int index, SearchModePreferencesEditorWindow mode)
    {
        var shortType = new Dictionary<string, string> { { "osa", "osa" }, { "tmp", "textmeshpro" } };
        if (string.IsNullOrEmpty(searchText))
            return true;

        if (searchText == string.Format("#{0}", index))
            return true;

        if (searchText == "#None") //TODO 其他的warning标签(<see cref="ControlItemDrawer.Draw:71"/>)也能查找到
        {
            if (!IsHasTarget())
                return true;
        }

        var lowSearchText = searchText.ToLower();
        if (mode == SearchModePreferencesEditorWindow.变量名)
        {
            if (name.ToLower().Contains(lowSearchText))
                return true;
        }
        else if (mode == SearchModePreferencesEditorWindow.路径)
        {
            if (targetsPath.ToLower().Contains(lowSearchText))
                return true;
        }
        else if (mode == SearchModePreferencesEditorWindow.类型)
        {
            if (type.ToLower() == lowSearchText)
                return true;

            if (shortType.ContainsKey(lowSearchText) && type.ToLower().Contains(shortType[lowSearchText]))
                return true;
        }
        else
        {
            if (name.ToLower().Contains(lowSearchText))
                return true;

            if (targetsPath.ToLower().Contains(lowSearchText))
                return true;

            if (extraFunc[BINDNULLINDEX].ToLower().Contains(lowSearchText))
                return true;

            if (type.ToLower() == lowSearchText)
                return true;

            if (shortType.ContainsKey(lowSearchText) && type.ToLower().Contains(shortType[lowSearchText]))
                return true;

            for (int i = 0; i < BindAllTypes.Count; ++i)
            {
                var flag = BindAllTypes[i];
                var flagName = Enum.GetName(typeof(BindType), flag).ToLower();
                if (GetBindFlag(flag) && flagName == lowSearchText)
                {
                    return true;
                }
            }
        }

        return false;
    }
    #endregion EditorTools

    public string DebugPrint()
    {
        return (" > PrintOneData: " + " ,index: " + index + " ,name: " + name + " ,type: " + type
         + " ,targetsPath: " + targetsPath + " ,TargetFileID: " + TargetFileID);
    }

    public void Copy(CtrlItemData copyFrom)
    {
        index = copyFrom.index;
        TargetFileID = copyFrom.TargetFileID;
        name = copyFrom.name;
        arg = copyFrom.arg;
        Array.Copy(copyFrom.extraGeneratorFunc, extraGeneratorFunc, copyFrom.extraGeneratorFunc.Length);
        Array.Copy(copyFrom.extraFunc, extraFunc, copyFrom.extraFunc.Length);
        isList = copyFrom.isList;
        listName = copyFrom.listName;
        isNotGeneratorCode = copyFrom.isNotGeneratorCode;
        type = copyFrom.type;
        targetsPath = copyFrom.targetsPath;
    }
}